/*
* Copyright Technophobia Ltd 2012
*
* This file is part of Substeps Maven Runner.
*
* Substeps Maven Runner is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Substeps Maven Runner is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Substeps. If not, see <http://www.gnu.org/licenses/>.
*/
package com.technophobia.substeps.runner;
import java.util.List;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.DependencyResolutionRequiredException;
import org.apache.maven.artifact.factory.ArtifactFactory;
import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
import org.apache.maven.artifact.resolver.ArtifactResolver;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.MavenProjectBuilder;
import com.technophobia.substeps.execution.node.RootNode;
import com.technophobia.substeps.report.ExecutionReportBuilder;
/**
* Mojo to run a number SubStep features, each contained within any number of
* executionConfigs, encapsulating the required config and setup and tear down
* details
*
* @goal run-features
* @requiresDependencyResolution test
* @phase integration-test
*
* @configurator include-project-dependencies
*/
@SuppressWarnings("unchecked")
public class SubstepsRunnerMojo extends AbstractMojo {
/**
*
* See <a href="./executionConfig.html">ExecutionConfig</a>
*
* @parameter
*/
private List<ExecutionConfig> executionConfigs;
/**
* The execution report builder you wish to use
*
* @parameter
*/
private final ExecutionReportBuilder executionReportBuilder = null;
/**
* When running in forked mode, a port is required to communicate between
* maven and substeps, to set explicitly use -DjmxPort=9999
*
* @parameter default-value="9999" expression="${jmxPort}"
*/
private Integer jmxPort;
/**
* A space delimited string of vm arguments to pass to the forked jvm
*
* @parameter
*/
private String vmArgs = null;
/**
* if true a jvm will be spawned to run substeps otherwise substeps will
* execute within the same jvm as maven
*
* @parameter default-value=true;
* @required
*/
private boolean runTestsInForkedVM;
/**
* List of classes containing step implementations e.g.
* <param>com.technophobia.substeps.StepImplmentations<param>
*
* @parameter
*/
private List<String> stepImplementationArtifacts;
/**
* @parameter default-value="${project}"
* @required
* @readonly
*/
private MavenProject project;
private final BuildFailureManager buildFailureManager = new BuildFailureManager();
/**
* @component
*/
private ArtifactResolver artifactResolver;
/**
* @component
*/
private ArtifactFactory artifactFactory;
/**
* @component
*/
private MavenProjectBuilder mavenProjectBuilder;
/**
* @parameter default-value="${localRepository}"
* @readonly
*/
private org.apache.maven.artifact.repository.ArtifactRepository localRepository;
/**
* @parameter default-value="${project.remoteArtifactRepositories}"
* @readonly
*/
private List remoteRepositories;
/**
* @parameter expression="${plugin.artifacts}"
* @readonly
*/
private List<Artifact> pluginDependencies;
/**
* @component
*/
private ArtifactMetadataSource artifactMetadataSource;
private MojoRunner runner;
public void execute() throws MojoExecutionException, MojoFailureException {
assertCompatibleCoreVersion();
ensureValidConfiguration();
this.runner = this.runTestsInForkedVM ? createForkedRunner() : createInProcessRunner();
executeConfigs();
processBuildData();
this.runner.shutdown();
}
private void assertCompatibleCoreVersion() throws MojoExecutionException {
CoreVersionChecker.assertCompatibleVersion(getLog(), this.artifactFactory, this.artifactResolver,
this.remoteRepositories, this.localRepository, this.mavenProjectBuilder, this.project,
this.pluginDependencies);
}
private ForkedRunner createForkedRunner() throws MojoExecutionException {
try {
return new ForkedRunner(getLog(), this.jmxPort, this.vmArgs, this.project.getTestClasspathElements(),
this.stepImplementationArtifacts, this.artifactResolver, this.artifactFactory,
this.mavenProjectBuilder, this.localRepository, this.remoteRepositories,
this.artifactMetadataSource);
} catch (final DependencyResolutionRequiredException e) {
throw new MojoExecutionException("Unable to resolve dependencies", e);
}
}
private InProcessRunner createInProcessRunner() {
return new InProcessRunner(getLog());
}
private void executeConfigs() throws MojoExecutionException {
if (this.executionConfigs == null || this.executionConfigs.isEmpty()) {
throw new MojoExecutionException("executionConfigs cannot be null or empty");
}
try {
for (final ExecutionConfig executionConfig : this.executionConfigs) {
runExecutionConfig(executionConfig);
}
} catch (final Throwable t) {
// to cater for any odd exceptions thrown out.. at least this way
// jvm shouldn't just die, unless it was going to die anyway
throw new MojoExecutionException("Unhandled exception: " + t.getMessage(), t);
}
}
private void runExecutionConfig(final ExecutionConfig theConfig) throws MojoExecutionException {
this.runner.prepareExecutionConfig(theConfig.asSubstepsExecutionConfig());
final RootNode rootNode = this.runner.run();
if (theConfig.getDescription() != null) {
rootNode.setLine(theConfig.getDescription());
}
addToReport(rootNode);
this.buildFailureManager.addExecutionResult(rootNode);
}
private void addToReport(final RootNode rootNode) {
if (this.executionReportBuilder != null) {
this.executionReportBuilder.addRootExecutionNode(rootNode);
}
}
/**
*
* @throws MojoFailureException
*/
private void processBuildData() throws MojoFailureException {
if (this.executionReportBuilder != null) {
this.executionReportBuilder.buildReport();
}
if (this.buildFailureManager.testSuiteFailed()) {
throw new MojoFailureException("Substep Execution failed:\n"
+ this.buildFailureManager.getBuildFailureInfo());
} else if (!this.buildFailureManager.testSuiteCompletelyPassed()) {
// print out the failure string (but won't include any failures)
getLog().info(this.buildFailureManager.getBuildFailureInfo());
}
}
private void ensureValidConfiguration() throws MojoExecutionException {
ensureForkedIfStepImplementationArtifactsSpecified();
}
private void ensureForkedIfStepImplementationArtifactsSpecified() throws MojoExecutionException {
if (this.stepImplementationArtifacts != null && !this.stepImplementationArtifacts.isEmpty()
&& !this.runTestsInForkedVM) {
throw new MojoExecutionException(
"Invalid configuration of substeps runner, if stepImplementationArtifacts are specified runTestsInForkedVM must be true");
}
}
}